In this challenge, you would need to write a type that takes an array and emitted the flatten array type.
在這個挑戰中,你需要編寫一個類型,接收一個陣列並輸出扁平化後的陣列類型。
type flatten = Flatten<[1, 2, [3, 4], [[[5]]]]> // [1, 2, 3, 4, 5]
你的任務是讓下面的type cases測試通過:
type cases = [
Expect<Equal<Flatten<[]>, []>>,
Expect<Equal<Flatten<[1, 2, 3, 4]>, [1, 2, 3, 4]>>,
Expect<Equal<Flatten<[1, [2]]>, [1, 2]>>,
Expect<Equal<Flatten<[1, 2, [3, 4], [[[5]]]]>, [1, 2, 3, 4, 5]>>,
Expect<Equal<Flatten<[{ foo: 'bar', 2: 10 }, 'foobar']>, [{ foo: 'bar', 2: 10 }, 'foobar']>>,
]
// @ts-expect-error
type error = Flatten<'1'>
從以下幾個方向來思考:
遞迴解法(Recursive Approach): 透過遞迴,我們可以逐層解析嵌套的陣列,並將內層的元素一層一層地取出並組合起來。每次判斷陣列的首元素是否還是陣列,如果是,繼續展開;如果不是,將其加入結果。
陣列操作(Array Operations): 我們需要通過類型擴展運算符 (...)
來將陣列中的元素展開,並使用條件類型來遞迴處理剩餘的陣列,直到所有元素都被展開。
解法:
type Flatten<T extends any[], K extends any[] = []> =
T extends [infer F, ...infer R]
? F extends any[]
? Flatten<[...F, ...R], K>
: Flatten<[...R], [...K, F]>
: K
類型定義 (Type Definition):
Flatten<T extends any[], K extends any[] = []>
:
T
代表我們要處理的嵌套陣列類型,並且要求 T
必須是一個陣列 (T extends any[]
)。這樣可以確保只對陣列進行扁平化處理。
K
是用來存放結果的累積陣列,默認情況下初始化為空陣列 (K = []
),用於遞迴過程中逐步累加扁平化的結果。
條件類型與遞迴 (Conditional Types and Recursion):
T extends [infer F, ...infer R]
:
這裡使用了 TypeScript 的條件類型和推斷 (infer
),來判斷 T
是否為一個非空陣列。如果 T
是一個非空陣列,它將被分解為兩部分:F
表示陣列的第一個元素,R
表示剩餘的元素。
如果 T
是空陣列(即已遞迴處理到陣列的末尾),條件類型將自動跳到 K
,並返回最終的累積結果。
F extends any[]
:
判斷 F
是否為一個陣列,如果是的話,我們需要繼續扁平化這個陣列。因此,透過運算符 ...
將 F
和 R
展開為單一陣列,並遞迴處理該新陣列。這樣可以確保 F
的內部元素也被展開並逐步處理。
累積結果 (Accumulate Result):
Flatten<R, [...K, F]>
:
當 F
不是一個陣列時(即 F
是一個基本類型或物件等),我們將其添加到累積結果陣列 K
中。
使用展開運算符 [...]
將 K
和 F
拼接起來,生成新的累積結果。
然後,對剩餘的元素 R
進行遞迴處理,直到陣列 T
被完全處理為止。
基礎情況(終止條件)(Base Case - Termination Condition):
當陣列 T
為空時,即遞迴過程結束,表示我們已經處理完所有元素。在這種情況下,遞迴停止,並返回最終的累積結果 K
。
額外補充:
這樣,我們就能順利通過測試啦 🎉 😭 🎉
本次介紹了Flatten
的實作,下一關會挑戰 Append to object
,期待再相見!